package com.star.truffle.core.eureka;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration;
import org.springframework.cloud.netflix.eureka.server.EurekaServerInitializerConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import com.netflix.appinfo.ApplicationInfoManager;

import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;


@Slf4j
@Configuration
@ConditionalOnClass({EurekaServerInitializerConfiguration.class})
@AutoConfigureAfter({EurekaServerAutoConfiguration.class})
public class EurekaAutoConfiguration extends WebMvcConfigurationSupport {

  @Autowired
  private EurekaInstanceConfigBean eurekaInstanceConfigBean;

  @Autowired
  private ObjectMapper objectMapper;

  @Autowired
  private RequestMappingHandlerMapping requestMappingHandlerMapping;

  @Autowired
  private ApplicationInfoManager applicationInfoManager;

  @PostConstruct
  public void initEurekaMetadata() throws JsonProcessingException {
    Map<String, String> metadata = Maps.newHashMap();
    metadata.put("framework-version", "0.1.2-SNAPSHOT");
    metadata.put("app-uptime", LocalDateTime.now().toString());
    metadata.put("app-apis", objectMapper.writeValueAsString(getRequestMetadata()));
    eurekaInstanceConfigBean.setMetadataMap(metadata);
    applicationInfoManager.registerAppMetadata(metadata);
    log.info("register metadata success, metadata is {}", metadata);
  }

  private List<EurekaRequestMetadata> getRequestMetadata() {
    String contextPath = getServletContext().getContextPath();
    Map<RequestMappingInfo, HandlerMethod> map = this.requestMappingHandlerMapping.getHandlerMethods();
    List<EurekaRequestMetadata> datas = map.entrySet().stream().filter(entry -> Arrays.stream(entry.getValue().getMethod().getDeclaredAnnotations()).anyMatch(annotation -> annotation.annotationType() == StarGateway.class)).flatMap(entry -> {
      return entry.getKey().getPatternsCondition().getPatterns().stream().flatMap(pattern -> {
        if(! pattern.startsWith("/")) {
          pattern += "/" + pattern;
        }
        String temp = pattern;
        List<String> methods = entry.getKey().getMethodsCondition().getMethods().stream().map(RequestMethod::name).collect(Collectors.toList());
        StarGateway hnRequestMetadata = entry.getValue().getMethod().getAnnotation(StarGateway.class);
        List<String> originPathList = Arrays.stream(hnRequestMetadata.gateway()).collect(Collectors.toList());
        if (originPathList.isEmpty()) {
          return Arrays.asList(createMetadata(methods, null, contextPath + temp, entry.getValue())).stream();
        }else {
          return originPathList.stream().map(originPath -> {
            return createMetadata(methods, originPath, contextPath + temp, entry.getValue());
          });
        }
      });
    }).collect(Collectors.toList());
    return datas;
  }
  
  private EurekaRequestMetadata createMetadata(List<String> methods, String gateway, String realPath, HandlerMethod method) {
    EurekaRequestMetadata eurekaRequestMetadata;
    if (methods.contains("GET")) {
      eurekaRequestMetadata = EurekaRequestMetadata.builder().realPath(realPath).methods(methods).type("query").build();
    } else {
      boolean body = Arrays.stream(method.getMethod().getParameterAnnotations()).anyMatch(annotations -> Arrays.stream(annotations).anyMatch(annotation -> annotation.annotationType() == RequestBody.class));
      String type = body ? "body" : "form";
      eurekaRequestMetadata = EurekaRequestMetadata.builder().realPath(realPath).methods(methods).type(type).build();
    }

//    ApiOperation apiOperation = method.getMethodAnnotation(ApiOperation.class);
//    String desc = null != apiOperation && StringUtils.isNotBlank(apiOperation.value()) ? apiOperation.value() : (null != apiOperation && StringUtils.isNotBlank(apiOperation.notes()) ? apiOperation.notes() : method.getMethod().getName());
//    eurekaRequestMetadata.setDesc(desc);
    eurekaRequestMetadata.setGateway(gateway);
    return eurekaRequestMetadata;
  }
}
